<template>
  <div v-if="!closed"
       class="textarea-item i-editor-card" :id="'card-' + id"
       v-loading="loading"
       @click.stop>
    <template v-if="url && type">
      <!-- vimeo 视频 -->
      <div class="i-editor-card-site" v-if="type === 'vimeoVideo'">
        <a class="i-editor-card-link" target="_blank" :href="url">
          <div
              style="float: left;position: relative;max-height: 90px;min-width: 90px;max-width: calc(107px);background-color: #000000;border-radius: 4px 0 0 4px;">
            <img class="i-editor-card-video-play" src="@/assets/img/standard/video_play.png"/>
            <img class="i-editor-card-link-icon" :src="vimeoLinkInfo.cover"/>
          </div>
          <div class="i-editor-card-link-text">
            <span class="editor-card-link-origin">vimeo.com</span>
            <span class="editor-card-link-describe">{{ vimeoLinkInfo.description }}</span>
          </div>
        </a>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon lg-icon-close"></i>
        </el-button>
      </div>
      <!-- youtube 视频 -->
      <div class="i-editor-card-site" v-if="type === 'youtubeVideo'">
        <a class="i-editor-card-link" target="_blank" :href="url">
          <div
              style="float: left;position: relative;max-height: 90px;min-width: 90px;max-width: calc(107px);background-color: #000000;border-radius: 4px 0 0 4px;">
            <img class="i-editor-card-video-play" src="@/assets/img/standard/video_play.png"/>
            <img class="i-editor-card-link-icon" :src="youtubeVideoLinkInfo.cover"/>
          </div>
          <div class="i-editor-card-link-text">
            <span class="editor-card-link-origin">{{ youtubeVideoLinkInfo.title }}</span>
            <span class="editor-card-link-describe">{{ youtubeVideoLinkInfo.description }}</span>
          </div>
        </a>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon lg-icon-close"></i>
        </el-button>
      </div>
      <!-- Google Book -->
      <div class="i-editor-card-site" v-if="type === 'googleBook'">
        <a contenteditable="false" class="i-editor-card-link" target="_blank" :href="url"
           @click.stop>
          <div style="float: left;;border-radius: 4px 0 0 4px;">
            <img v-if="googleBookLinkInfo.cover && googleBookLinkInfo.cover.length > 0"
                 class="i-editor-card-link-icon" :src="googleBookLinkInfo.cover">
          </div>
          <div class="i-editor-card-link-text">
            <span class="editor-card-link-origin">{{ googleBookLinkInfo.title }}</span>
            <span class="editor-card-link-describe">{{ googleBookLinkInfo.description }}</span>
          </div>
        </a>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon lg-icon-close"></i>
        </el-button>
      </div>
      <!-- 普通网站 -->
      <div class="i-editor-card-site" v-if="type === 'site'">
        <a contenteditable="false" class="i-editor-card-link" target="_blank" :href="url"
           @click.stop>
          <div style="float: left;border-radius: 4px 0 0 4px;">
            <img v-if="commentSiteLinkInfo.icon && commentSiteLinkInfo.icon.length > 0"
                 referrerpolicy="no-referrer"
                 class="i-editor-card-link-icon" v-show="isImgShow" :src="commentSiteLinkInfo.icon"
                 @load="isImgShow = true">
          </div>
          <div class="i-editor-card-link-text">
            <span class="editor-card-link-origin">{{ commentSiteLinkInfo.title }}</span>
            <span class="editor-card-link-describe">{{ commentSiteLinkInfo.description }}</span>
          </div>
        </a>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon lg-icon-close"></i>
        </el-button>
      </div>
      <!--  图片链接  -->
      <div class="i-editor-card-img" v-if="type === 'img'">
        <img referrerpolicy="no-referrer" style="width: 192px;height: 108px;" class="i-editor-card-link"
             :src="imgLinkInfo.url"/>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon lg-icon-close"></i>
        </el-button>
      </div>
    </template>
    <template v-else>
      <!-- 加载中    -->
      <div class="i-editor-card-site" contenteditable="false"
           v-if="loading" v-loading="loading">
        <div class="i-editor-card-link">
        </div>
        <el-button :id="'remove-' + id" :cardId="id" class="i-editor-card-close" @click="handleClickClose"
                   type="text">
          <i :id="'remove-' + id" class="lg-icon  lg-icon-close"></i>
        </el-button>
      </div>
    </template>
  </div>
</template>

<script>
import EditorTools from "@/utils/EditorTools";

export default {
  name: "LinkCard",
  props: ['link', 'cardClose', 'id'], // 需要解析的链接
  computed: {
    loading() {
      return !this.url || !this.type
    }
  },
  data() {
    return {
      // 识别出的链接
      url: '',
      // 识别中
      analyzing: false,
      // 分析后，链接类型
      type: '', // '': 无链接、img: 图片链接、vimeoVideo、site: web 网站
      // 图片类型的链接信息
      imgLinkInfo: {
        url: ''
      },
      vimeoLinkInfo: { // vimeo 视频链接信息
        source: '',
        description: '',
        cover: '',
      },
      youtubeVideoLinkInfo: {  // youtubeVideo 视频链接信息
        title: '',
        description: '',
        cover: '',
      },
      googleBookLinkInfo: {  // Google Book 视频链接信息
        source: '',
        title: '',
        description: '',
        cover: '',
      },
      commentSiteLinkInfo: {  // 普通网站链接信息
        title: '',
        description: '',
        icon: '',
        type: ''
      },
      // 关闭状态
      closed: false,
      isImgShow: false
    }
  },
  watch: {
    // 内容变更时，进行分析
    link: {
      immediate: true,
      handler(value) {
        let reg = /(((https?):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|win|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2})))|(www\.)+[a-zA-Z0-9-]+\.(com|win|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'@\\+&%$#=~_-]+))*/ig
        let matches = value && value.match(reg) || []
        let url = matches.slice(-1)[0]
        if (url && !/(http)/i.test(url)) { // 以 www 开头的链接要手动拼接 https:// 前缀
          url = 'https://' + url
        }
        this.url = url || ''
      }
    },
    url: {
      immediate: true,
      handler(value) {
        this.analysisLink(value)
        this.analyzing = false
        if (this.url && this.type) {
          this.closed = false
        }
      }
    },
  },
  methods: {
    // 分析链接信息
    async analysisLink(link) {
      if (!link) {
        this.$emit('set-link-info', null)
        return
      }
      let url = new URL(link)
      if (!link && url) {
        this.type = ''
        return
      }
      // 判断是否是 vimeo 视频
      let linkInfo = await this.getVimeoInfo(link);
      if (linkInfo) {
        this.type = 'vimeoVideo';
        this.vimeoLinkInfo = linkInfo
        this.$emit('set-link-info', this.vimeoLinkInfo)
        return
      }
      linkInfo = await this.getGoogleBookInfo(link);
      if (linkInfo) {
        this.type = 'googleBook';
        this.googleBookLinkInfo = linkInfo
        this.$emit('set-link-info', this.googleBookLinkInfo)
        return
      }
      linkInfo = await this.getCommonSiteInfo(link)
      if (linkInfo) {
        if (this.type === 'img') {
          this.imgLinkInfo = {url: link}
          this.$emit('set-link-info', this.imgLinkInfo)
          return
        }
        if (this.type === 'youtubeVideo') {
          this.youtubeVideoLinkInfo = linkInfo
          this.$emit('set-link-info', this.youtubeVideoLinkInfo)
          return
        }
        if (this.type === 'site') {
          this.commentSiteLinkInfo = linkInfo
          this.$emit('set-link-info', this.commentSiteLinkInfo)
          return
        }
      }
      this.type = 'site';
      this.commentSiteLinkInfo = {
        title: new URL(link).host,
        description: link,
        icon: '',
        type: 'link'
      }
      this.$emit('set-link-info', this.commentSiteLinkInfo)
    },

    handleClickClose() {
      // this.cardClose(this.id)
      // this.closed = true
      // this.$emit('cardClose', this.id)
      // this.$emit('set-link-info', null)
    },
    // 获取 vimeo 视频信息
    async getVimeoInfo(link) {
      // 从链接中提取视频 ID
      let parsed = link.match(/(?:www\.|player\.)?vimeo.com\/(?:channels\/(?:\w+\/)?|groups\/(?:[^\/]*)\/videos\/|album\/(?:\d+)\/video\/|video\/|)(\d+)(?:[a-zA-Z0-9_\-]+)?/i)
      let vimeoID = parsed && parsed[1]
      if (!vimeoID || !new URL(link).host.toLowerCase().includes('vimeo')) {
        return
      }
      this.analyzing = true;
      return Promise.race([
        fetch('https://vimeo.com/api/v2/video/' + vimeoID + '.json'),
        new Promise(function (resolve, reject) {
          setTimeout(() => reject(new Error('request timeout')), 4000)
        })])
          .then((response) => response.json())
          .then(result => {
            if (result[0] && result.length > 0) {
              let description = ''
              if (result[0].description) {
                let doc = new DOMParser().parseFromString(result[0].description, 'text/html')
                description = doc.querySelector('body').innerText
              }
              return {
                source: result,
                description: description, // 网站描述
                cover: result[0].thumbnail_medium, // 视频封面
              }
            }
          }).catch((e) => {
            // console.log('vimeo.com api Request Failed', e)
            return
          })
    },
    // 获取 Google Book 信息
    async getGoogleBookInfo(link) {
      // 从链接中提取视频 ID
      let id = EditorTools.getGoogleBookID(link)
      if (!id) {
        return
      }
      this.analyzing = true
      return Promise.race([
        fetch('https://www.googleapis.com/books/v1/volumes/' + id),
        new Promise(function (resolve, reject) {
          setTimeout(() => reject(new Error('request timeout')), 4000)
        })])
          .then((response) => response.json())
          .then(result => {
            if (result && result.volumeInfo.title) {
              let description
              let title
              if (result.volumeInfo.description) {
                title = result.volumeInfo.title
                let doc = new DOMParser().parseFromString(result.volumeInfo.description, 'text/html')
                description = doc.querySelector('body').innerText
              } else {
                title = 'books.google.com'
                description = result.volumeInfo.title
              }
              let cover = result.volumeInfo.imageLinks.small || result.volumeInfo.imageLinks.thumbnail || result.volumeInfo.imageLinks.medium
              // console.log('google book cover', cover)
              cover = (cover && cover.startsWith('http://')) && 'https' + cover.substr(4, cover.length) || cover
              return {
                source: result,
                title: title,
                description: description,
                cover: cover,
              }
            }
          }).catch((e) => {
            // console.log('Google Book Request Failed', e)
            return
          })
    },
    // 获取普通网站信息
    async getCommonSiteInfo(link) {
      // 从链接中提取视频 ID
      let youTubeID = EditorTools.getYouTubeID(link)
      this.analyzing = true;
      const server = 'https://api.allorigins.win/get?url='
      return Promise.race([
        fetch(server + link),
        new Promise(function (resolve, reject) {
          setTimeout(() => reject(new Error('request timeout')), 4000)
        })])
          .then(response => response.json())
          .then(result => {
            if (!result || !result.contents || (result.status.http_code !== 200 && result.status.http_code !== 304)) {
              // console.log('common Link Request Failed', result)
              return
            }
            let type = result.status.content_type
            if (type.includes('image')) {
              this.type = 'img'
              return {url: link}
            }
            if (!type.includes('text/html')) {
              return
            }
            let html = result.contents
            let title, icon
            let url = new URL(link)
            const doc = new DOMParser().parseFromString(html, 'text/html')
            // If there is no title, no card link is generated
            title = doc.querySelector('title')
            let commentSiteLinkInfo
            if (title) {
              title = title.textContent
              // 是 YouTube 并且 存在 视频 ID
              if (youTubeID && url.host.toLowerCase().includes('youtu')) {
                // console.log('Find YouTube Video Id', youTubeID)
                let cover = 'https://i3.ytimg.com/vi/' + youTubeID + '/hqdefault.jpg'
                this.type = 'youtubeVideo'
                return {
                  title: 'youtube.com',
                  description: title,
                  cover: cover,
                }
              }
              // Get the src of the first img tag in the body tag
              icon = doc.querySelector('body img')
              icon = icon && icon.getAttribute('src')
              if (/^data:image/.test(icon)) icon = ''
              // If there is no src then get the site icon
              if (!icon) {
                const links = [].slice.call(doc.querySelectorAll('link[rel][href]'))
                icon = links.find((_el) => _el.rel.includes('icon'))
                icon = icon && icon.getAttribute('href')
              }
              // If `icon` is not the ['https://', 'http://', '//'] protocol, splice on the `origin` of the a tag
              if (icon && !EditorTools.isHttp_icon(icon)) {
                if (!new RegExp('^/').test(icon)) {
                  icon = '/' + icon
                }
                icon = url.origin + icon
              }
              if (icon && icon.toLowerCase().endsWith(".gif")) {
                icon = ''
              }
              commentSiteLinkInfo = {
                title: url.host,
                description: title,
                icon: icon,
                type: 'site'
              }
            }
            if (doc && !commentSiteLinkInfo) {
              commentSiteLinkInfo = {
                title: url.host,
                description: link,
                icon: '',
                type: 'site'
              }
            }
            this.type = 'site'
            return commentSiteLinkInfo
          })
          .catch(e => {
            // console.log('common Link Identify Error', e)
            return null
          })
    },
  }
}
</script>

<style lang="less" scoped>
.textarea-item {
  word-break: break-all;
}

/deep/ .el-loading-mask {
  border-radius: 5px;
  z-index: 1 !important;
}

.i-editor-card-link {
  display: flex;
  flex-direction: row;
  width: 450px;
  align-items: center;
  height: 90px;
  background-color: #fff;;
  margin: 0;
  border-radius: 5px;
  gap: 10px;
}

.i-editor-card-link .i-editor-card-link-icon {
  max-width: calc(107px);
  min-width: 90px;
  height: 90px;
  min-height: 90px;
  overflow: hidden;
  margin: 0;
  padding: 0;
  object-fit: contain;
  border-radius: 5px 0 0 5px;
}

.i-editor-card-link {
  cursor: pointer !important;
}

.i-editor-card-link .i-editor-card-link-text {
  display: flex;
  flex-direction: column;
  align-content: flex-start;
  gap: 10px;
  flex: 1;
  flex-shrink: 0;
  margin: 0;
  padding-right: 10px;
  max-width: 430px;
}

.i-editor-card {
  margin-top: 5px;
  margin-bottom: 10px;
}

.i-editor-card-site {
  width: max-content;
  position: relative;
  border: 1px solid #c0c4cc;
  border-radius: 5px;
  background-color: #fff;
}

.i-editor-card-close {
  cursor: pointer;
  position: absolute;
  top: -6px;
  right: -8px;
  margin: 0;
  padding: 1px;
  background-color: #888888;
  border-radius: 100%;
  width: 18px;
  height: 18px;
  z-index: 10;
}

/deep/ .i-editor-card-close .lg-icon-close {
  color: #ffffff;
  font-size: 18px;
}

.editor-card-link-describe {
  color: #606266;
  font-size: 14px;
  font-weight: bold;
  overflow: hidden;
  text-overflow: ellipsis;
  word-wrap: break-word;
  word-break: break-word;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.editor-card-link-origin {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #606266;
  font-size: 16px;
}


.i-editor-card-img {
  position: relative;
  width: 192px;
  height: 108px;
}

.i-editor-card-img img {
  width: 192px;
  height: 108px;
  cursor: initial;
}

.i-editor-card-video-play {
  width: 40px;
  height: 40px;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
}
</style>